`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 按行缓冲数据
*/

module rgb_line_buf (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // input pixel
    input  wire         I_frame_start,
    input  wire         I_line_start,  // 行开始
    input  wire         I_line_end,    // 行结束
    input  wire [9:0]   I_line_num,    // 行号
    input  wire         I_pixel_valid, // 像素有效
    input  wire [9:0]   I_pixel_col,   // 像素列坐标
    input  wire [23:0]  I_pixel_gray,  // 像素数据
    // line output
    output wire         O_row_start,   // 单周期脉冲，表明一行数据正在缓冲中，此时可以预读逐点调整系数
    output wire [9:0]   O_row_num,     // 当前行号 
    output wire         O_row_ready,   // 电平有效，表明本行已缓冲完毕，可以读取数据
    input  wire         I_row_ack,     // 当前行数据使用完毕
    input  wire         I_pixel_req,   // 请求读取本行数据（顺序读取）
    input  wire [9:0]   I_pixel_addr,
    output wire [23:0]  O_pixel_data   // 像素数据
    
);
//------------------------Parameter----------------------
// fsm
localparam [1:0]
    IDLE  = 0,
    READY = 1,
    ACK   = 2;


//------------------------Local signal-------------------
// fsm
reg  [1:0]  state;
reg  [1:0]  next;
reg         start_pending;
reg         end_pending;
reg         skip_row;
reg         ram_pending;

// ram
wire        ram0_wren;
wire [9:0]  ram0_waddr;
wire [23:0] ram0_data;
wire        ram0_rden;
wire [9:0]  ram0_raddr;
wire [23:0] ram0_q;
wire        ram1_wren;
wire [9:0]  ram1_waddr;
wire [23:0] ram1_data;
wire        ram1_rden;
wire [9:0]  ram1_raddr;
wire [23:0] ram1_q;
reg         ram_wsel;
reg         ram_rsel;
wire        ram_rden;
reg  [9:0]  ram_raddr;

// portrait
wire [4:0]  cfg_port_max;
reg  [4:0]  port_id;
reg  [9:0]  next_scan_addr;

// line output
reg  [2:0]  color;
wire [23:0]  pixel_buf;
reg         row_start;
reg  [9:0]  row_num;

//------------------------Instantiation------------------
// // sdpram_1024x24
sdpram_1024x24 ram0 (
    .data      ( ram0_data ),
    .rdaddress ( ram0_raddr ),
    .rdclock   ( I_sclk ),
    .rden      ( ram0_rden ),
    .wraddress ( ram0_waddr ),
    .wrclock   ( I_sclk ),
    .wren      ( ram0_wren ),
    .q         ( ram0_q )
);
// sdpram_lpm #(
    // .A_ADDRESS_WIDTH    (   10   ),
    // .A_DATA_WIDTH       (   24   ),
    // .B_ADDRESS_WIDTH    (   10   ),
    // .B_DATA_WIDTH       (   24   )
    // )
// ram0(
    // .clka       (   I_sclk      ),
    // .wea        (   ram0_wren    ),
    // .addra      (   ram0_waddr   ),
    // .dina       (   ram0_data    ),

    // .clkb       (   I_sclk      ),
    // .reb        (   ram0_rden    ),
    // .addrb      (   ram0_raddr   ),
    // .doutb      (   ram0_q       )
    
// );



// // sdpram_1024x24
sdpram_1024x24 ram1 (/*{{{*/
    .data      ( ram1_data ),
    .rdaddress ( ram1_raddr ),
    .rdclock   ( I_sclk ),
    .rden      ( ram1_rden ),
    .wraddress ( ram1_waddr ),
    .wrclock   ( I_sclk ),
    .wren      ( ram1_wren ),
    .q         ( ram1_q )
);/*}}}*/

// sdpram_lpm #(
    // .A_ADDRESS_WIDTH    (   10   ),
    // .A_DATA_WIDTH       (   24   ),
    // .B_ADDRESS_WIDTH    (   10   ),
    // .B_DATA_WIDTH       (   24   )
    // )
// ram1(
    // .clka       (   I_sclk      ),
    // .wea        (   ram1_wren    ),
    // .addra      (   ram1_waddr   ),
    // .dina       (   ram1_data    ),

    // .clkb       (   I_sclk      ),
    // .reb        (   ram1_rden    ),
    // .addrb      (   ram1_raddr   ),
    // .doutb      (   ram1_q       )
    
// );


//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else if (I_frame_start)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if (I_line_start | start_pending)
                next = READY;
            else
                next = IDLE;
        end

        READY: begin
            if (I_line_end | end_pending)
                next = ACK;
            else
                next = READY;
        end

        ACK: begin
            if (I_row_ack)
                next = IDLE;
            else
                next = ACK;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

// start_pending
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        start_pending <= 1'b0;
    else if (I_frame_start)
        start_pending <= 1'b0;
    else if (state == IDLE)
        start_pending <= 1'b0;
    else if (I_line_start)
        start_pending <= 1'b1;
end

// end_pending
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        end_pending <= 1'b0;
    else if (I_frame_start)
        end_pending <= 1'b0;
    else if (state == READY)
        end_pending <= 1'b0;
    else if (I_line_end & ~skip_row)
        end_pending <= 1'b1;
end

// skip_row
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        skip_row <= 1'b0;
    else if (I_frame_start)
        skip_row <= 1'b0;
    else if (I_line_start)
        skip_row <= start_pending;
end

// ram_pending
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        ram_pending <= 1'b0;
    else if (I_line_start && ~start_pending)
        ram_pending <= ~ram_wsel;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++ram++++++++++++++++++++++++++++
assign ram0_wren  = (ram_wsel == 1'b0) && ~skip_row && I_pixel_valid;
assign ram0_waddr = I_pixel_col;
assign ram0_data  = I_pixel_gray;
assign ram0_rden  = ram_rden;
assign ram0_raddr = I_pixel_addr;

assign ram1_wren  = (ram_wsel == 1'b1) && ~skip_row && I_pixel_valid;
assign ram1_waddr = I_pixel_col;
assign ram1_data  = I_pixel_gray;
assign ram1_rden  = ram_rden;
assign ram1_raddr = I_pixel_addr;

assign ram_rden = I_pixel_req ;

// ram_wsel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        ram_wsel <= 1'b0;
    else if (I_line_start && ~start_pending)
        ram_wsel <= ~ram_wsel;
end

// ram_rsel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        ram_rsel <= 1'b0;
    else if (state == IDLE) begin
        if (start_pending)
            ram_rsel <= ram_pending;
        else if (I_line_start)
            ram_rsel <= ~ram_wsel;
    end
end

//{{{+++++++++++++++++++++line output++++++++++++++++++++
assign O_row_start  = row_start;
assign O_row_num    = row_num;
assign O_row_ready  = (state == ACK);
// assign O_pixel_data = pixel_buf;
assign O_pixel_data = ram_rsel == 1'b0 ?ram0_q[23:0]:ram1_q[23:0];
 
 
// // pixel_buf
// always @(*) begin
    // if (ram_rsel == 1'b0) begin
        // pixel_buf <= ram0_q[23:0];
    // end
    // else begin
        // pixel_buf <= ram1_q[23:0];
    // end
// end

// row_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        row_start <= 1'b0;
    else if (state == IDLE && next == READY)
        row_start <= 1'b1;
    else
        row_start <= 1'b0;
end

// row_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        row_num <= 1'b0;
    else if (I_line_start && ~start_pending)
        row_num <= I_line_num;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++


endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
